using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace TfsSpam.Utilities
{
    /// <summary>
    /// The TfsUtility class provides a number of simple convenience
    /// methods and properties for getting information out of TFS.
    /// </summary>
    public class TfsUtility
    {
        #region -- Private class members
        /// <summary>
        /// The name of the TFS server to work with.
        /// </summary>
        private readonly string _serverName;

        /// <summary>
        /// The name of the TFS project information should be
        /// retrieved from.
        /// </summary>
        private readonly string _projectName;
        #endregion

        #region -- Constructor
        /// <summary>
        /// Standard constructor for the class.
        /// </summary>
        /// <param name="serverName">The name of the TFS server to connect to.</param>
        /// <param name="projectName">The name of the TFS project information should be looked up from.</param>
        public TfsUtility( string serverName, string projectName )
        {
            _serverName = serverName;
            _projectName = projectName;
        }
        #endregion

        #region -- Work item-related methods
        /// <summary>
        /// Given an area path, this method returns the corresponding area id.
        /// </summary>
        /// <param name="areaPath">The path of the area whose id should be retrieved.</param>
        /// <returns>The id of the specified area or <c>-1</c> if the area can't be found.</returns>
        public int GetAreaId( string areaPath )
        {
            if ( AreaIds.ContainsKey( areaPath ) )
            {
                return AreaIds[ areaPath ];
            }
            else
            {
                return -1;
            }
        }

        /// <summary>
        /// Retrieves the <see cref="WorkItemType"/> with the specified
        /// name in the TFS project.
        /// </summary>
        /// <param name="typeName">The name of the requested <see cref="WorkItemType"/>.</param>
        /// <returns>The requested <see cref="WorkItemType"/>.</returns>
        public WorkItemType GetWorkItemType( string typeName )
        {
            return Project.WorkItemTypes[ typeName ];
        }

        /// <summary>
        /// Retrieves the <see cref="WorkItem"/> with the specified id.
        /// </summary>
        /// <param name="id">The id of the requested work item.</param>
        /// <returns></returns>
        public WorkItem GetWorkItem( int id )
        {
            return Store.GetWorkItem( id );
        }

        /// <summary>
        /// Executes the specified query and returns the matching
        /// work items.
        /// </summary>
        /// <param name="query">The work item query to execute.</param>
        /// <returns>The collection of <see cref="WorkItem"/> objects that match the query.</returns>
        public WorkItemCollection GetWorkItems( string query )
        {
            return Store.Query( query );
        }

        /// <summary>
        /// Creates a work item of the specified type in the project
        /// we're connected to.
        /// </summary>
        /// <param name="type">The type of work item that should be created.</param>
        /// <param name="bugFields">The values that should be defined for the new work item.</param>
        /// <returns>The created work item.</returns>
        /// <exception cref="ArgumentException">Thrown if <paramref name="type"/> isn't a valid work item type in the project.</exception>
        public WorkItem CreateBug( WorkItemType type, Dictionary<string,string> bugFields )
        {
            if ( Project.WorkItemTypes.Contains( type ) )
            {
                WorkItem wi = new WorkItem( type );

                foreach ( string fieldName in bugFields.Keys )
                {
                    string fieldValue = bugFields[ fieldName ];
                    Field field = wi.Fields[ fieldName ];

                    field.Value = fieldValue;
                }

                foreach ( Field field in wi.Fields )
                {
                    if ( !field.IsValid )
                    {
                        if ( field.IsLimitedToAllowedValues )
                        {
                            field.Value = field.AllowedValues[ 0 ];
                        }
                    }
                }

                wi.Save();
                return wi;
            }
            else
            {
                throw new ArgumentException( Properties.Strings.TfsUtility_InvalidWorkItemType, "type" );
            }
        }
        #endregion

        #region -- Area-related properties and methods
        /// <summary>
        /// The dictionary containing the mappings between area paths
        /// and area ids.
        /// </summary>
        protected Dictionary<string, int> AreaIds
        {
            get
            {
                if ( _areaIds == null )
                {
                    _areaIds = LookupAreaIds();
                }

                return _areaIds;
            }
        } private Dictionary<string, int> _areaIds;

        /// <summary>
        /// Looks up the ids and paths of all areas defined in the TFS
        /// project and returns a Dictionary containing the mappings
        /// between area paths and ids.
        /// </summary>
        /// <returns>A dictionary containing the mappings between area paths and ids.</returns>
        private Dictionary<string, int> LookupAreaIds()
        {
            Dictionary<string, int> areaIds = new Dictionary<string, int>();

            foreach ( Node rootArea in Project.AreaRootNodes )
            {
                LookupChildAreaIds( rootArea, areaIds );
            }

            return areaIds;
        }

        /// <summary>
        /// Looks up the ids and paths of all child areas to the specified area
        /// and adds that to the dictionary (along with the id and path for <paramref name="area"/>.)
        /// </summary>
        /// <param name="area">The area whose child areas should be processed.</param>
        /// <param name="areaIds">The dictionary containing the area ids and paths looked up so far.</param>
        private void LookupChildAreaIds( Node area, Dictionary<string,int> areaIds )
        {
            areaIds[ area.Path ] = area.Id;
            foreach ( Node childArea in area.ChildNodes )
            {
                LookupChildAreaIds( childArea, areaIds );
            }
        }
        #endregion

        #region -- General TFS-related properties
        /// <summary>
        /// The TFS project we're working with.
        /// </summary>
        protected Project Project
        {
            get
            {
                if ( _project == null )
                {
                    // We haven't looked up our project yet, do so now
                    // by looking at all the projects we have access to
                    // until we find it.
                    foreach ( Project project in Store.Projects )
                    {
                        if ( project.Name == _projectName )
                        {
                            _project = project;
                            break;
                        }
                    }
                }

                return _project;
            }
        } private Project _project;

        /// <summary>
        /// The work item store on the TFS server we're connected to.
        /// </summary>
        private WorkItemStore Store
        {
            get
            {
                if ( _store == null )
                {
                    _store = (WorkItemStore) Server.GetService( typeof( WorkItemStore ) );
                }

                return _store;
            }
        } private WorkItemStore _store;

        /// <summary>
        /// The TFS server we're connected to.
        /// </summary>
        private TeamFoundationServer Server
        {
            get
            {
                if ( _server == null )
                {
                    _server = TeamFoundationServerFactory.GetServer( _serverName );
                }

                return _server;
            }
        } private TeamFoundationServer _server;
        #endregion
    }
}
